// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved
// Developer:  Michael Antonio
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Reflection;

namespace MichMan.Utilities
{
    /// <summary>
    /// Some stuff that should be part of the framework, IMO.
    /// </summary>
    public class ParseUtility
    {
        private static Regex isGuid = new Regex( @"^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$");
        private static readonly object _syncRoot = new object();

        public static T SafeParse<T>(string candidate)
        {
            return SafeParse<T>(candidate, default(T));
        }

        public static T SafeParse<T>(string candidate, T defaultValue)
        {
            T value;
            if (ParseUtility.TryParse<T>(candidate, out value))
            {
                return value;
            }
            return defaultValue;
        }

        static Dictionary<Type, MethodInfo> _tryParseMethods = null;

        public static bool TryParseType(Type type, string input, out object value)
        {
            MethodInfo mig = typeof(ParseUtility).GetMethod("TryParse").GetGenericMethodDefinition();
            MethodInfo mi = mig.MakeGenericMethod(type);
            object[] parms = new object[] { input, null };
            bool retval = (bool)mi.Invoke(null, parms);
            value = parms[1];
            return retval;
        }

        public static bool TryParse<T>(string input, out T value)
        {
            if (String.IsNullOrEmpty(input))
            {
                value = default(T);
                return false;
            }

            Type literalType = typeof(T);
            if (literalType.IsEnum)
            {
                return EnumUtility.InternalTryParse<T>(input, true, out value);
            }

            if (_tryParseMethods == null)
            {
                // Don't take the cost of locking unless we need to.  That's why test/lock/test.
                lock (_syncRoot)
                {
                    if (_tryParseMethods == null)
                    {
                        _tryParseMethods = new Dictionary<Type, MethodInfo>();
                        _tryParseMethods.Add(typeof(Guid), typeof(ParseUtility).GetMethod("TryParseGuid", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static));
                    }
                }
            }

            if (!_tryParseMethods.ContainsKey(literalType))
            {
                // Don't take the cost of locking unless we need to.  That's why test/lock/test.
                lock (_syncRoot)
                {
                    if (!_tryParseMethods.ContainsKey(literalType))
                    {
                        // See if there is an appropriate TryParse for this type.
                        var methods = from method in literalType.GetMethods()
                                      where method.Name == "TryParse" &&
                                              method.IsStatic && method.ReturnType == typeof(bool) &&
                                              method.GetParameters().Count() == 2 &&
                                              method.GetParameters()[0].ParameterType == typeof(string) &&
                                              method.GetParameters()[1].ParameterType == literalType.MakeByRefType()
                                      select method;

                        if (methods.Count() != 1)
                        {
                            value = default(T);
                            return false;
                        }
                        _tryParseMethods.Add(literalType, methods.First());
                    }
                }
            }

            object[] args = new object[] { input, null };
            bool returnValue = (bool)_tryParseMethods[literalType].Invoke(null, args);
            value = (T)args[1];
            return returnValue;
        }

        private static bool TryParseGuid( string candidate, out Guid output )
        {
            bool isValid = false;
            output = Guid.Empty;

            if ( false == string.IsNullOrEmpty(candidate) )
            {
                if ( isGuid.IsMatch( candidate ) )
                {
                    output = new Guid( candidate );
                    isValid = true;
                }
            }

            return isValid;
        }
    }
}

